﻿// ------------------------------------------------------------------------
//    YATSE - A touch screen remote controller for XBMC (.NET 3.5)
//    Copyright (C) 2010  Tolriq (http://code.google.com/p/yatse/)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;
using System.Collections;

namespace YATSE.Libs
{

    [StructLayout(LayoutKind.Sequential)]
    public struct Devmode
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;
        public short dmOrientation;
        public short dmPaperSize;
        public short dmPaperLength;
        public short dmPaperWidth;
        public short dmScale;
        public short dmCopies;
        public short dmDefaultSource;
        public short dmPrintQuality;
        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmFormName;
        public short dmUnusedPadding;
        public short dmBitsPerPel;
        public int dmPelsWidth;
        public int dmPelsHeight;
        public int dmDisplayFlags;
        public int dmDisplayFrequency;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayDevice
    {
        public int cb;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString;
        public int StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;

        public DisplayDevice(int flags)
        {
            cb = 0;
            StateFlags = flags;
            DeviceName = new string(Convert.ToChar(" "), 32);
            DeviceString = new string(Convert.ToChar(" "), 128);
            DeviceID = new string(Convert.ToChar(" "), 128);
            DeviceKey = new string(Convert.ToChar(" "), 128);
            cb = Marshal.SizeOf(this);
        }
    }

    class ScreenResolution
    {

        public static bool CompareDevMode (Devmode a, Devmode b)
        {
            return a.dmBitsPerPel == b.dmBitsPerPel 
                   &&a.dmCollate == b.dmCollate
                   &&a.dmColor == b.dmColor
                   &&a.dmCopies == b.dmCopies
                   &&a.dmDefaultSource == b.dmDefaultSource
                   &&a.dmDeviceName == b.dmDeviceName
                   &&a.dmDisplayFlags == b.dmDisplayFlags
                   &&a.dmDisplayFrequency == b.dmDisplayFrequency
                   &&a.dmDriverExtra == b.dmDriverExtra
                   &&a.dmDriverVersion == b.dmDriverVersion
                   &&a.dmDuplex == b.dmDuplex
                   &&a.dmFields == b.dmFields
                   &&a.dmFormName == b.dmFormName
                   &&a.dmOrientation == b.dmOrientation
                   &&a.dmPaperLength == b.dmPaperLength
                   &&a.dmPaperSize == b.dmPaperLength
                   &&a.dmPaperSize == b.dmPaperSize
                   &&a.dmPaperWidth == b.dmPaperWidth
                   &&a.dmPelsHeight == b.dmPelsHeight
                   &&a.dmPelsWidth == b.dmPelsWidth
                   &&a.dmPrintQuality == b.dmPrintQuality
                   &&a.dmScale == b.dmScale
                   &&a.dmSize == b.dmSize
                   &&a.dmSpecVersion == b.dmSpecVersion
                   &&a.dmTTOption == b.dmTTOption
                   &&a.dmUnusedPadding == b.dmUnusedPadding
                   &&a.dmYResolution == b.dmYResolution;
        }

        public void ChangeResolution(int devNum, int modeNum)
        { 
            var d = GetDevmode(devNum, modeNum);
            if (d.dmBitsPerPel != 0 && d.dmPelsWidth != 0 && d.dmPelsHeight != 0)
            {
                ChangeDisplaySettingsEx(GetDeviceName(devNum), ref d, IntPtr.Zero, 0, IntPtr.Zero);
            }
        }

        public void ChangeResolutionMode(int devNum, Devmode mode)
        { 
            if (mode.dmBitsPerPel != 0 && mode.dmPelsWidth != 0 && mode.dmPelsHeight != 0)
            {
                ChangeDisplaySettingsEx(GetDeviceName(devNum), ref mode, IntPtr.Zero, 0, IntPtr.Zero);
            }
        }
        public int[] EnumDevices()
        { 
            var devices = new ArrayList();
            var d = new DisplayDevice(0);
            var devNum = 0;
            bool result;

            do
            {
                result = EnumDisplayDevices(IntPtr.Zero, devNum, ref d, 0);
                if (result)
                {
                    devices.Add(devNum);
                }
                devNum++;
            } while (result);
            return (int[])devices.ToArray(typeof(int));
        }

        public Devmode[] EnumModes(int devNum)
        {
            var modes = new ArrayList();
            var devName = GetDeviceName(devNum);
            var devMode = new Devmode();
            var modeNum = 0;
            bool result;

            do
            {
                result = EnumDisplaySettings(devName, modeNum, ref devMode);
                if (result)
                {
                    modes.Add(devMode);

                }
                modeNum++;
            } while (result);

            return (Devmode[])modes.ToArray(typeof(Devmode));
        }


        public Devmode GetDevmode(int devNum, int modeNum)
        { 
            var devMode = new Devmode();
            var devName = GetDeviceName(devNum);
            EnumDisplaySettings(devName, modeNum, ref devMode);
            return devMode;
        }

        public static string DevmodeToString(Devmode devMode)
        {
            return devMode.dmPelsWidth +
                   " x " + devMode.dmPelsHeight +
                   ", " + devMode.dmBitsPerPel +
                   " bits, " +
                   devMode.dmDisplayFrequency + " Hz";
        }

        public string GetDeviceName(int devNum)
        {
            var d = new DisplayDevice(0);
            var result = EnumDisplayDevices(IntPtr.Zero, devNum, ref d, 0);
            return (result ? d.DeviceName.Trim() : "#error#");
        }

        public string GetDeviceString(int devNum)
        {
            var d = new DisplayDevice(0);
            var result = EnumDisplayDevices(IntPtr.Zero, devNum, ref d, 0);
            return (result ? d.DeviceString.Trim() : "#error#");
        }
        
        public bool MainDevice(int devNum)
        { 
            var d = new DisplayDevice(0);
            if (EnumDisplayDevices(IntPtr.Zero, devNum, ref d, 0))
            {
                return ((d.StateFlags & 4) != 0);
            } 
            return false;
        }


        [DllImport("User32.dll")]
        private static extern bool EnumDisplayDevices(
            IntPtr lpDevice, int iDevNum,
            ref DisplayDevice lpDisplayDevice, int dwFlags);

        [DllImport("User32.dll")]
        private static extern bool EnumDisplaySettings(
            string devName, int modeNum, ref Devmode devMode);

        [DllImport("user32.dll")]
        public static extern int ChangeDisplaySettings(
            ref Devmode devMode, int flags);

        [DllImport("user32.dll")]
        public static extern int ChangeDisplaySettingsEx(
            string devName, ref Devmode devMode, IntPtr hwnd, int dwFlags, IntPtr lParam);


    }
}


